from django.utils.translation import gettext_lazy, gettext as _
from django.http import HttpResponseRedirect
from rest_framework.serializers import Serializer
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.request import Request
from drf_yasg.utils import swagger_auto_schema, no_body
from drf_yasg import openapi

from core import errors
from utils.iprestrict import IPRestrictor
from apps.api.viewsets import NormalGenericViewSet
from apps.app_auth_provider.managers import AllowedRedirectManager, AuthProviderManager, AuthRecordManager
from apps.app_auth_provider.providers import build_auth_provider
from apps.app_auth_provider.models import AuthProviderType


class AuthorizeViewSet(NormalGenericViewSet):
    permission_classes = []
    pagination_class = None
    lookup_field = 'id'

    @swagger_auto_schema(
        tags=['auth_provider'],
        operation_summary=gettext_lazy('获取登录地址'),
        request_body=no_body,
        manual_parameters=[
            openapi.Parameter(
                name='redirect_uri',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_STRING,
                required=True,
                description=gettext_lazy('认证完成后的回调地址')
            ),
            openapi.Parameter(
                name='provider_type',
                in_=openapi.IN_PATH,
                type=openapi.TYPE_STRING,
                required=True,
                description=gettext_lazy('认证服务提供者'),
                enum=AuthProviderType.values
            ),
        ],
        responses={
            200: ''''''
        }
    )
    @action(methods=['POST'], detail=False, url_path=r'(?P<provider_type>[^/.]+)', url_name='auth-url')
    def auth_url(self, request, *args, **kwargs):
        """
        获取登录地址

            http code 200:
            {
                "auth_url":"https://xxx"
            }

            http code 400、403、409、500:
            {
                "code": "AccessDenied",
                "message": "回调地址没有认证权限，请联系工作人员添加白名单。"
            }

            error code:
                InvalidArgument: 参数有误；
                Conflict: 此认证服务未提供，请选择其他认证方式。
        """
        redirect_uri = request.query_params.get('redirect_uri', None)
        provider_type = kwargs['provider_type']

        if not redirect_uri:
            return self.exception_response(
                exc=errors.InvalidArgument(message=_('redirect_uri不能为空，请填写认证完成后的回调地址。')))

        try:
            auth_url = self.get_auth_url(request=request, provider_type=provider_type, redirect_uri=redirect_uri)
        except Exception as exc:
            return self.exception_response(exc=exc)

        return Response(data={'auth_url': auth_url})

    @swagger_auto_schema(
        tags=['auth_provider'],
        operation_summary=gettext_lazy('获取登录地址，并直接跳转'),
        manual_parameters=[
            openapi.Parameter(
                name='redirect_uri',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_STRING,
                required=True,
                description=gettext_lazy('认证完成后的回调地址')
            ),
            openapi.Parameter(
                name='provider_type',
                in_=openapi.IN_PATH,
                type=openapi.TYPE_STRING,
                required=True,
                description=gettext_lazy('认证服务提供者'),
                enum=AuthProviderType.values
            ),
        ],
        responses={
            302: ''''''
        }
    )
    @action(methods=['GET'], detail=False, url_path=r'(?P<provider_type>[^/.]+)/redirect', url_name='auth-url-redirect')
    def auth_url_redirect(self, request, *args, **kwargs):
        """
        获取登录地址，并直接跳转


            http code 302 redirect

            http code 400、403、409、500:
            {
                "code": "AccessDenied",
                "message": "回调地址没有认证权限，请联系工作人员添加白名单。"
            }

            error code:
                InvalidArgument: 参数有误；
                Conflict: 此认证服务未提供，请选择其他认证方式。
        """
        redirect_uri = request.query_params.get('redirect_uri', None)
        provider_type = kwargs['provider_type']

        if not redirect_uri:
            return self.exception_response(
                exc=errors.InvalidArgument(message=_('redirect_uri不能为空，请填写认证完成后的回调地址。')))

        try:
            auth_url = self.get_auth_url(request=request, provider_type=provider_type, redirect_uri=redirect_uri)
        except Exception as exc:
            return self.exception_response(exc=exc)

        return HttpResponseRedirect(redirect_to=auth_url)

    @staticmethod
    def get_auth_url(request: Request, provider_type: str, redirect_uri: str) -> str:
        """
        raises: Error
        """
        ok, exc = AllowedRedirectManager.check_allowed_redirect_uri(redirect_uri)
        if not ok:
            raise exc

        auth_provider = AuthProviderManager.get_auth_provider(provider_type=provider_type)
        if auth_provider is None:
            raise errors.ConflictError(message=_('此认证服务未提供，请选择其他认证方式。'))

        if not auth_provider.is_active:
            raise errors.ConflictError(message=_('此认证服务已暂停服务，请选择其他认证方式。'))

        provider = build_auth_provider(auth_provider=auth_provider)
        if provider is None:
            raise errors.InvalidArgument(message=_('未支持第三方认证服务。'))

        # 创建认证记录和中继参数
        removte_ip, proxy_ips = IPRestrictor.get_remote_ip(request=request)
        host = request.get_host()

        try:
            record = AuthRecordManager.create_auth_record(
                provider_type=provider_type,
                redirect_uri=redirect_uri,
                remote_ip=removte_ip,
                hostname=host
            )
        except Exception as e:
            raise errors.Error(message=_('认证记录创建失败。') + str(e))

        try:
            auth_url = provider.build_authentication_url(
                state=str(record.id)
            )
        except Exception as e:
            raise errors.Error(message=_('请求第三方认证服务错误，构建登录url失败。') + str(e))

        return auth_url

    def get_serializer_class(self):
        return Serializer
